﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Input;
using Foundation;
using Foundation.ExtensionMethods;
using Poet.Views;

namespace Poet.ViewModels
{
    [DataContract]
    internal class TrunkViewModel : RootViewModel
    {
        public TrunkViewModel()
        {
            Initialize();
        }

        public ICommand Show { get; private set; }

        public ICommand SetNotification { get; private set; }

        public ObservableCollection<DocumentView> DocumentViews { get; private set; }

        public Icon TrayIcon
        {
            get { return Icon.ExtractAssociatedIcon(InstanceManager.ExecutableFilePath); }
        }

        public TrayNotifyLevel TrayNotifyLevel
        {
            get { return Get(() => TrayNotifyLevel); }
            set { Set(() => TrayNotifyLevel, value); }
        }

        public object ActiveContent
        {
            get { return Get(() => ActiveContent); }
            set { Set(() => ActiveContent, value); }
        }

        public object ActiveDocumentView
        {
            get { return Get(() => ActiveDocumentView); }
            set { Set(() => ActiveDocumentView, value); }
        }

        [DataMember]
        public bool IsShown
        {
            get { return Get(() => IsShown); }
            set { Set(() => IsShown, value); }
        }

        [DataMember]
        public bool ShowInTray
        {
            get { return Get(() => ShowInTray, true); }
            set { Set(() => ShowInTray, value); }
        }

        [DataMember]
        public bool ShowInTaskbar
        {
            get { return Get(() => ShowInTaskbar, true); }
            set { Set(() => ShowInTaskbar, value); }
        }

        [DataMember]
        public WindowState WindowState
        {
            get { return Get(() => WindowState); }
            set { Set(() => WindowState, value); }
        }

        [DataMember]
        private WindowState LastWindowState { get; set; }

        private bool LastTaskbarState { get; set; }

        public void OnFirstTimeActivated(object sender, EventArgs eventArgs)
        {
            var mainView = (TrunkView) sender;
            var args = AppLoader.Current.StartupEventArgs.Args;
            if (args.Any()) OpenDocuments(args);
            else AddDocument();
            mainView.Activated -= OnFirstTimeActivated;

            if (AssociationViewModel.IsAssotiationConfigurationFileExists) return;
            RecentFileManagerViewModel.Clear();
            ApplicationCommands.Properties.Execute("SetAssociations", Application.Current.MainWindow);
        }

        [OnDeserialized]
        private void Initialize(StreamingContext context = default(StreamingContext))
        {
            SetNotification = new RoutedCommand();
            Executed();
            CanExecute();
            OpenFileDialog.Multiselect = true;
            LastTaskbarState = ShowInTaskbar;
            DocumentViews = new ObservableCollection<DocumentView>();
            Show = new SimpleCommand(parameter => IsShown = bool.Parse((string) parameter));
            TrayNotifyLevel = TrayNotify.GetLevel(InstanceManager.ExecutableFilePath);
            OnPropertyChanged();
            InstanceManager.OnReciveArguments += OpenDocuments;
            ApplicationClosing += () =>
                {
                    IsShown = true;
                    WindowState = WindowState.Normal;
                    this.TrySerializeDataContract();
                };
        }

        private void CanExecute()
        {
            this[ApplicationCommands.Find].CanExecute += (o, args) => args.CanExecute = args.Parameter == null;
            this[ApplicationCommands.Replace].CanExecute += (o, args) => args.CanExecute = args.Parameter == null;
            this[ApplicationCommands.Open].CanExecute +=
                (o, args) =>
                args.CanExecute =
                args.Parameter == null || args.Parameter is string[];
            this[ApplicationCommands.Save].CanExecute +=
                (o, args) =>
                args.CanExecute =
                Equals(args.Parameter, "All") && DocumentViews.Any(v => ApplicationCommands.Save.CanExecute(null, v));

            this[SetNotification].CanExecute += (o, args) =>
                {
                    var notificationSettings = (TrayNotifyLevel) int.Parse((string) args.Parameter);
                    args.CanExecute = TrayNotifyLevel != notificationSettings;
                };

            this[ApplicationCommands.Close].CanExecute += (o, args) =>
                {
                    var parameter = args.Parameter;
                    if (Equals(parameter, "All"))
                        args.CanExecute = DocumentViews.Any(v => ApplicationCommands.Close.CanExecute(null, v));
                    else if (Equals(parameter, "App")) args.CanExecute = true;
                    else if (Equals(parameter, null)) args.CanExecute = false;
                };
        }

        private void Executed()
        {
            this[ApplicationCommands.Find].Executed += OnSearch;
            this[ApplicationCommands.Replace].Executed += OnSearch;
            this[SetNotification].Executed +=
                (o, args) => TrayNotifyLevel = (TrayNotifyLevel) int.Parse((string) args.Parameter);
            this[ApplicationCommands.New].Executed +=
                (o, args) => AddDocument().ViewModel.TextEditorViewModel.Text = args.Parameter as string;


            this[ApplicationCommands.Open].Executed += (o, args) =>
                {
                    var parameters =
                        args.Parameter is string
                            ? new[] {(string) args.Parameter}
                            : args.Parameter as string[];
                    if (parameters != null)
                        OpenDocuments(parameters);
                    else if (OpenFileDialog.ShowDialog() == true)
                        OpenDocuments(OpenFileDialog.FileNames);

                };

            this[ApplicationCommands.Save].Executed += (o, args) =>
                {
                    var unsavedDocuments =
                        DocumentViews.Where(v => ApplicationCommands.Save.CanExecute(null, v)).ToList();
                    if (unsavedDocuments.
                            Select(dv => dv.DataContext).
                            Cast<DocumentViewModel>().
                            Count(d => string.IsNullOrEmpty(d.FileName)) < 2)
                    {
                        foreach (var documentView in unsavedDocuments)
                            ApplicationCommands.Save.Execute(null, documentView);
                    }
                    else
                    {
                        var viewModel = Store.OfType<SaveViewModel>();
                        viewModel.SetItems(DocumentViews, DocumentViews);
                        var view = new SaveView {DataContext = viewModel, Owner = Application.Current.MainWindow};
                        view.ShowDialog();
                    }
                };

            this[ApplicationCommands.Close].Executed += (o, args) =>
                {
                    IsShown = true;
                    if (DocumentViews.Any(v => ApplicationCommands.Save.CanExecute(null, v)))
                    {
                        var viewModel = Store.OfType<SaveViewModel>();
                        viewModel.SetItems(DocumentViews, DocumentViews);
                        var view = new SaveView {DataContext = viewModel, Owner = Application.Current.MainWindow};
                        if (view.ShowDialog() == false || Equals(args.Parameter, "All")) return;
                    }

                    DocumentViews.ToList().ForEach(v => ApplicationCommands.Close.Execute("DontSave", v));
                    DocumentViews.Clear();
                    ApplicationClosing();
                    ShowInTray = false;
                    Environment.Exit(0);
                };
        }

        private static void OnSearch(object o, ExecutedRoutedEventArgs args)
        {
            //var mainView = (TrunkView) Application.Current.MainWindow;
            //var searchAndReplaceView = ((TrunkView) Application.Current.MainWindow).SearchAndReplaceView;
            //searchAndReplaceView.Show(mainView.DockingManager);
            //searchAndReplaceView.Activate();
            //var searchAndReplaceTabsView = (SearchTabbedView) searchAndReplaceView.Content;

            //if (args.Command == ApplicationCommands.Find)
            //    searchAndReplaceTabsView.TabItem1.Focus();
            //if (args.Command == ApplicationCommands.Replace)
            //    searchAndReplaceTabsView.TabItem2.Focus();
        }

        private void OnPropertyChanged()
        {
            this[() => ShowInTaskbar].PropertyChanged += (sender, args) => { if (!ShowInTaskbar && !ShowInTray) ShowInTray = true; };
            this[() => ShowInTray].PropertyChanged += (sender, args) => { if (!ShowInTray && !ShowInTaskbar) ShowInTaskbar = true; };
            this[() => WindowState].PropertyChanged +=
                (sender, args) => { if (WindowState != WindowState.Minimized) LastWindowState = WindowState; };

            this[() => IsShown].PropertyChanged += (sender, args) =>
                {
                    if (IsShown)
                    {
                        ShowInTaskbar = LastTaskbarState;
                        WindowState = LastWindowState;
                        if (WindowState == WindowState.Minimized)
                            ShowInTaskbar = true;
                        Application.Current.MainWindow.Activate();
                    }
                    else
                    {
                        if (WindowState != WindowState.Minimized)
                            LastWindowState = WindowState;
                        LastTaskbarState = ShowInTaskbar;
                        ShowInTaskbar = true;
                        WindowState = WindowState.Minimized;
                        ShowInTaskbar = LastTaskbarState;
                        if (ShowInTray) ShowInTaskbar = false;
                    }
                };

            this[() => TrayNotifyLevel].PropertyChanged += (sender, args) =>
                {
                    var oldValue = TrayNotify.GetLevel(InstanceManager.ExecutableFilePath);
                    var newValue = TrayNotify.SetLevel(InstanceManager.ExecutableFilePath, TrayNotifyLevel);
                    if (Equals(newValue, oldValue)) return;
                    TrayNotify.RestartExplorer();
                };
        }

        private void OpenDocuments(string[] fileNames)
        {
            var documentViewModels = DocumentViews.Select(v => v.DataContext).OfType<DocumentViewModel>().ToList();
            DocumentView documentView = null;
            foreach (var fileName in fileNames)
            {
                var documentViewModel = documentViewModels.FirstOrDefault(d => d.FileName == fileName);
                documentView =
                    documentViewModel == null
                        ? AddDocument(fileName)
                        : DocumentViews.First(dv => dv.DataContext == documentViewModel);
            }

            if (documentView != null)
                documentView.Activate();

            IsShown = true;
        }

        private DocumentView AddDocument(string fileName = null)
        {
            lock (DocumentViews)
            {
                try
                {
                    var recentFileViewModel = RecentFileManagerViewModel.Add(fileName);
                    var view =
                        recentFileViewModel == null
                            ? new DocumentView {DataContext = new DocumentViewModel(fileName)}
                            : recentFileViewModel.RestoreView();
                    view.Closing += OnDocumentViewClosing;
                    view.Closed += (sender, args) => view.Closing -= OnDocumentViewClosing;
                    DocumentViews.Add(view);
                    Application.Current.Dispatcher.BeginInvoke(GC.Collect);
                    return view;
                }
                catch (Exception exception)
                {
                    var view = Application.Current.MainWindow;
                    MessageBox.Show(view, exception.Message, view.Title, MessageBoxButton.OK, MessageBoxImage.Error);
                    return null;
                }
            }
        }

        private void OnDocumentViewClosing(object sender, CancelEventArgs args)
        {
            var documentView = (DocumentView) sender;
            if (ApplicationCommands.Save.CanExecute(null, documentView))
            {
                var viewModel = Store.OfType<SaveViewModel>();
                viewModel.SetItems(DocumentViews, new List<DocumentView> {documentView});
                var view = new SaveView {DataContext = viewModel, Owner = Application.Current.MainWindow};
                args.Cancel = view.ShowDialog() != true;
            }

            var documentViewModel = documentView.DataContext as DocumentViewModel;
            if (documentViewModel == null) return;
            if (args.Cancel) return;
            Foundation.MarkupExtensions.Localizing.DisposeUselessLocalizings();
        }
    }
}